package com.pisolution.scheduler.remote.codec;

import com.pisolution.scheduler.remote.command.Command;
import com.pisolution.scheduler.remote.command.CommandType;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;

import javax.annotation.Nullable;
import java.util.List;

/**
 * 以特定的解码规则解码节点间通信传输的命令载体 编码规则详见 {@link CommandEncoder}.
 *
 * @see CommandEncoder
 * @author sora
 * @version 0.1.0
 */
public final class CommandDecoder extends ReplayingDecoder<CommandDecoder.State> {
    /** 交互命令类型. */
    private byte type;

    /** 命令的标识符. */
    private long opaque;

    /** 命令内容长度. */
    private int length;

    // -------------------------------------------------------------------------------------------------
    //  初始化
    // -------------------------------------------------------------------------------------------------

    public CommandDecoder() {
        super(State.MAGIC);
    }

    // -------------------------------------------------------------------------------------------------
    //  解码
    // -------------------------------------------------------------------------------------------------

    /**
     * <b>注意</b>.
     *
     * <p>由于父类的特殊性 切勿添加break语句到case分支中 这里需要借助switch语句的穿透特性.</p>
     */
    @SuppressWarnings("CheckStyle")
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) {
        switch (super.state()) {
            case MAGIC:
                // 1. 确认传输内容是否为载体命令.
                if (byteBuf.readByte() != Command.MAGIC) {
                    throw new IllegalArgumentException("The packet received could not resolved by the command decoder.");
                }

                // 2. 记录检查点.
                super.checkpoint(State.COMMAND);
            case COMMAND:
                this.type = byteBuf.readByte();

                // 3. 记录检查点.
                super.checkpoint(State.OPAQUE);
            case OPAQUE:
                this.opaque = byteBuf.readLong();

                // 4. 记录检查点.
                super.checkpoint(State.LENGTH);
            case LENGTH:
                this.length = byteBuf.readInt();

                // 5. 记录检查点.
                super.checkpoint(State.BODY);
            case BODY:
                // 6. 尝试从字节流中读取命令内容.
                final byte[] body = new byte[this.length];

                byteBuf.readBytes(body);

                // 7. 构建完整的载体命令.
                final Command command = new Command();

                command.setType(this.parseType(this.type));
                command.setOpaque(this.opaque);
                command.setBody(body);

                list.add(command);

                // 8. 记录检查点.
                this.checkpoint(State.MAGIC);

                break;
            default:
                throw new Error("Could not decode more contents.");
        }
    }

    /**
     * 将 {@link CommandType#ordinal()} 转换为 {@link CommandType}.
     *
     * @param type 待转换请求命令类型在该类型中的枚举序号.
     *
     * @return 转换之后的 {@link CommandType} 对象 如果转换失败则返回 {@code null}.
     */
    @Nullable
    private CommandType parseType(final byte type) {
        for (final CommandType item: CommandType.values()) {
            if (item.ordinal() == type) {
                return item;
            }
        }

        return null;
    }

    // -------------------------------------------------------------------------------------------------
    //  辅助类
    // -------------------------------------------------------------------------------------------------

    /**
     * 用于记录解析完成程度的工具类 如果不使用 {@link ReplayingDecoder} 可以移除该工具类.
     */
    enum State {
        MAGIC,
        COMMAND,
        OPAQUE,
        LENGTH,
        BODY
    }
}
